/*
 * Snake controller.
 */

#include <stdlib.h>
#include <iostream>
#include <Windows.h>
#include <time.h>
#include <glut.h>
#include <gl/GL.h>
#include <vector>
#include "Snake.h"

extern void display();

enum {
	UP,
	RIGHT,
	DOWN,
	LEFT
};

Snake::Snake()
{
	this->length = SNAKE_START_LENGTH;
	this->s = 10;
	this->n = UP;
	this->InitList();
	
	// init the apple
	srand(time(NULL)); // give srand a seed
	while(true)
	{
		apple.X() = rand() % 800;
		apple.Y() = rand() % 600;
		if(apple.X() % 20 == 0 && apple.Y() % 20 == 0 && apple.X() > 0 && 
			apple.X() < 800 && apple.Y() > 0 && apple.Y() < 600)
		{
			break;
		}
	}
}

void Snake::SnakeDisplay()
{
	glClear(GL_COLOR_BUFFER_BIT);
	for(int i = 0; i < this->size(); ++i)
	{
		// To prevent a draw bug.
		if((*this)[i].X() == 0 && (*this)[i].Y() == 0) {
			continue;
		}
		if(i == 0) {
			glColor3f(1, 0, 0);
		} else {
			glColor3f(1, 0.5, 0);
		}
		glRectf((*this)[i].X() - 10, (*this)[i].Y() - 10, 
			(*this)[i].X() + 10, (*this)[i].Y() + 10);
	}

	glColor3f(0, 1, 0);
	glRectf(this->apple.X() - 10, this->apple.Y() - 10,
		this->apple.X() + 10, this->apple.Y() + 10);

	glutSwapBuffers();
}

void Snake::SnakeUpdate()
{
	this->SnakeDisplay();
	if(n == UP && s % 10 == 0) 
	{
		for(int i = this->length - 1; i > 0; --i)
		{
			this->SnakeRelocate(i);
		}
		(*this)[0].Y() -= 20;
		this->s = 1;
	}

	if(n == RIGHT && s % 10 == 0) 
	{
		for(int i = this->length - 1; i > 0; --i)
		{
			this->SnakeRelocate(i);
		}
		(*this)[0].X() += 20;
		s = 1;
	}

	if(n == DOWN && s % 10 == 0) 
	{
		for(int i = length - 1; i > 0; --i)
		{
			SnakeRelocate(i);
		}
		(*this)[0].Y() += 20;
		s = 1;
	}

	if(n == LEFT && s % 10 == 0) 
	{
		for(int i = length - 1; i > 0; --i)
		{
			SnakeRelocate(i);
		}

		(*this)[0].X() -= 20;
		s = 1;
	}
	
	if((*this)[0].X() <= 0 || (*this)[0].Y() <= 0 || 
		(*this)[0].X() >= 800 || (*this)[0].Y() >= 600 || this->CheckSelfHit())
	{
		MessageBox(NULL, L"Game over!", L"Game over!", MB_OK);
		exit(0);
	}

	if(GetAsyncKeyState(VK_LEFT))
	{
		if(n != RIGHT)
			n = LEFT;
	}
	if(GetAsyncKeyState(VK_RIGHT))
	{
		if(n != LEFT)
			n = RIGHT;
	}
	if(GetAsyncKeyState(VK_UP))
	{
		if(n != DOWN)
			n = UP;
	}
	if(GetAsyncKeyState(VK_DOWN))
	{
		if(n != UP)
			n = DOWN;
	}

	if((*this)[0].X() == apple.X() && (*this)[0].Y() == apple.Y())
	{
		while(true)
		{
			apple.X() = rand() % 800;
			apple.Y() = rand() % 600;
			if(apple.X() % 20 == 0 && apple.Y() % 20 == 0 && apple.X() > 0 && 
				apple.X() < 800 && apple.Y() > 0 && apple.Y() < 600)
			{
				break;
			}
		}
		this->AddSnakeObject();
	}

	if (s < 10)
	{
		s++;
	}
}

int Snake::size()
{
	return this->objlist.size();
}

SnakeObject& Snake::operator[](int index)
{
	return this->objlist[index];
}

void Snake::SnakeRelocate(int index)
{
	(*this)[index].X() = (*this)[index-1].X();
	(*this)[index].Y() = (*this)[index-1].Y();
}

SnakeApple& Snake::GetSnakeApple()
{
	return this->apple;
}

void Snake::InitList()
{
	SnakeObject snake0(200, 200);
	this->objlist.push_back(snake0);
	SnakeObject *obj;
	int x; int y;

	for(int i = 1; i < SNAKE_START_LENGTH; i++) {
		x = this->objlist.at(i-1).X();
		y = this->objlist.at(i-1).Y();
		obj = new SnakeObject(x, y+20);
		this->objlist.push_back(*obj);
	}
}

void Snake::AddSnakeObject()
{
	int i = this->size() - 1;
	int x = this->objlist.at(i-1).X();
	int y = this->objlist.at(i-1).Y();
	SnakeObject *obj = new SnakeObject(x,y);
	this->objlist.push_back(*obj);
	this->length++;
}

bool Snake::CheckSelfHit()
{
	SnakeObject& head = this->objlist.at(0);
	bool ret = false;
	int idx = 1;

	for(; idx < this->size(); idx++) {
		if((*this)[idx].X() == head.X() && (*this)[idx].Y() == head.Y()) {
			ret = true;
			break;
		}
	}
	return ret;
}
